#ifndef _MATH_2D_H_
#define _MATH_2D_H_

#include <math.h>

inline float Math_abs(float a) {return (a >= 0.0f) ? a : -a;}
template<typename T>
inline T Math_min(T a, T b) {return a < b ? a: b;}
template<typename T>
inline T Math_max(T a, T b) {return a > b ? a : b;}

inline float Math_rad(float a) {return 0.0174532925222f * a;}
inline float Math_deg(float a) {return 57.295779505601f * a;}

inline float Math_sind(float a) {return sinf(Math_rad(a));}
inline float Math_asind(float r) {return Math_deg(asin(r));}
inline float Math_cosd(float a) {return cosf(Math_rad(a));}
inline float Math_acosd(float r) {return Math_deg(acos(r));}
inline float Math_tand(float a) {return tanf(Math_rad(a));}
inline float Math_atand2(float y, float x) {return Math_deg(atan2f(y, x));}

  inline bool Math_cmp(float a, float b, float err)
{
  return Math_abs(a - b) < err;
}
inline float Math_clamp(float n, float min, float max)
{
  if (n < min) return min;
  if (n > max) return max;
  
  return n;
}

inline float Math_angleWrap360(float a)
{
  while (a < 0.0f) a += 360.0f;
  while (a >= 360.0f) a -= 360.0f;
  
  return a;
}

inline float Math_angleWrap180(float a)
{
  while (a < -180.0f) a += 360.0f;
  while (a > 180.0f) a -= 360.0f;
  
  return a;
}

inline bool Math_angleCmp(float a1, float a2, float err)
{
  float a = Math_angleWrap180(a1 - a2);
  
  return Math_abs(a) < err;
}

bool Math_angleIsBetween(float a, float a1, float a2);

////////////////////////////////////////////////////////////////////////////////

class Vector2
{
  public:
  Vector2() {}
  Vector2(float x, float y): x(x), y(y) {}
  Vector2(const Vector2& v): x(v.x), y(v.y) {}
  
  float mag() const;
  void normalize();
  float angle() const;
  
  float dot(const Vector2& v) const;
  
  void operator+=(const Vector2& v);
  
  union
  {
    struct
    {
      float x;
      float y;
    };
    float element[2];
  };
};

Vector2 operator+(const Vector2& v1, const Vector2& v2);
Vector2 operator-(const Vector2& v1, const Vector2& v2);
Vector2 operator*(float n, const Vector2& v);
float Vector2_dis(const Vector2& v1, const Vector2& v2);
void Vector2_fromAngle(Vector2* v, float a);

////////////////////////////////////////////////////////////////////////////////

class Matrix3x2
{
  public:
  float a, b, c, d, e, f;
};

void Matrix3x2_genTranslation(Matrix3x2* m, float x, float y);

////////////////////////////////////////////////////////////////////////////////

class LineSegment
{
  public:
  
  
  Vector2 p1, p2;
};

////////////////////////////////////////////////////////////////////////////////

class Rectf
{
  public:
  void set(float min_x, float min_y, float max_x, float max_y)
  {
    this->min_x = min_x;
    this->min_y = min_y;
    this->max_x = max_x;
    this->max_y = max_y;
  }
  
  float min_x, min_y, max_x, max_y;
};

////////////////////////////////////////////////////////////////////////////////

class Circle
{
  public:
  Circle(){}
  Circle(const Vector2& p, float r)
  {
    pos = p;
    radius = r;
  }
  
  void set(float x, float y, float r)
  {
    pos.x = x;
    pos.y = y;
    radius = r;
  }
  void set(const Vector2& p, float r)
  {
    pos = p;
    radius = r;
  }
  
  bool contains(const Vector2& p) const;
  bool containedWithin(const Rectf& r) const;
  
  bool intersect(const Circle& c) const;
  bool intersect(const LineSegment& l) const;
  
  Vector2 pos;
  float radius;
};

////////////////////////////////////////////////////////////////////////////////

class Arc
{
  public:
  Arc() {}
  Arc(const Vector2& p, float r, float a1, float a2)
  {
    pos = p;
    radius = r;
    angle1 = Math_angleWrap360(a1);
    angle2 = Math_angleWrap360(a2);
  }
  
  bool contains(const Vector2& p);
  bool intersect(const Circle& c);
  
  Vector2 pos;
  float radius;
  float angle1, angle2;
};

////////////////////////////////////////////////////////////////////////////////

enum Shape2DType
{
  SHAPE_2D_POINT,
  SHAPE_2D_RECT,
  SHAPE_2D_CIRCLE,
  NUM_2D_SHAPES
};

class Shape2D
{
  public:
  Shape2D(const Vector2& point);
  Shape2D(const Rectf& rect);
  Shape2D(const Circle& circle);
  
  Shape2DType type() const {return _type;}
  
  bool containedWithin(const Shape2D& shape);
  
  private:
  Shape2DType _type;
  union
  {
    const Vector2* _point;
    const Rectf* _rect;
    const Circle* _circle;
  };
};

#endif
